/**
 * 一个帮助你管理列表状态，并能生成唯一 key 的 Hook。
 * ! 引用自 ahooks, 如果什么时候有引入这个库, 可以删除此文件, 并修改对应的引用. 版本号 ahooks 2.x, 8d81969
 */
import { useCallback, useRef, useState } from 'react';

// :: 感觉上主要解决了处理列表的时候没有可用的 key 值的问题, 其余的是简化了数组操作方法
export default <T>(initialValue: T[]) => {
    const counterRef = useRef(-1); // key值累加器
    // key 存储器
    const keyList = useRef<number[]>([]);

    // 内部方法
    const setKey = useCallback((index: number) => {
        counterRef.current += 1; // 累加 Key 值, 保持唯一
        keyList.current.splice(index, 0, counterRef.current);
    }, []);

    // 每次操作列表, 都会为每一项重新生成一个id
    const [list, setList] = useState(() => {
        (initialValue || []).forEach((_, index) => {
            setKey(index); // 初始化的时候, 也初始化 key 值列表
        });
        return initialValue || [];
    });

    // 重置成新的列表
    const resetList = useCallback((newList: T[] = []) => {
        keyList.current = [];
        counterRef.current = -1;
        setList(() => {
            (newList || []).forEach((_, index) => {
                setKey(index); // 遍历设置 key 值
            });
            return newList || [];
        });
    }, [setKey, setList]);

    // 插入
    const insert = (index: number, obj: T) => {
        setList(l => {
            const temp = [...l];
            temp.splice(index, 0, obj);
            setKey(index);
            return temp;
        });
    };

    // 获取 key 值, 在遍历的时候需要 key 值的时候, 可以用这个
    const getKey = (index: number) => keyList.current[index];
    // 获得某个key的 index
    const getIndex = (index: number) => keyList.current.findIndex(ele => ele === index);

    // 在指定位置插入多个元素
    const merge = (index: number, obj: T[]) => {
        setList(l => {
            const temp = [...l];
            obj.forEach((_, i) => {
                setKey(index + i);
            });
            temp.splice(index, 0, ...obj);
            return temp;
        });
    };

    // 替换指定元素
    const replace = (index: number, obj: T) => {
        setList(l => {
            const temp = [...l];
            temp[index] = obj;
            return temp;
        });
    };

    // 删除指定元素
    const remove = (index: number) => {
        setList(l => {
            const temp = [...l];
            temp.splice(index, 1);

            // remove keys if necessary
            try {
                keyList.current.splice(index, 1);
            } catch (e) {
                console.error(e);
            }
            return temp;
        });
    };

    const move = (oldIndex: number, newIndex: number) => {
        if (oldIndex === newIndex) {
            return;
        }
        setList(l => {
            const newList = [...l];
            const temp = newList.filter((_: {}, index: number) => index !== oldIndex);
            temp.splice(newIndex, 0, newList[oldIndex]);

            // move keys if necessary
            try {
                const keyTemp = keyList.current.filter((_: {}, index: number) => index !== oldIndex);
                keyTemp.splice(newIndex, 0, keyList.current[oldIndex]);
                keyList.current = keyTemp;
            } catch (e) {
                console.error(e);
            }

            return temp;
        });
    };

    const push = (obj: T) => {
        setList(l => {
            setKey(l.length);
            return l.concat([obj]);
        });
    };

    // :: 为什么操作key值的逻辑会和上面的不一致?
    const pop = () => {
        // remove keys if necessary
        try {
            keyList.current = keyList.current.slice(0, keyList.current.length - 1);
        } catch (e) {
            console.error(e);
        }

        setList(l => l.slice(0, l.length - 1));
    };

    const unshift = (obj: T) => {
        setList(l => {
            setKey(0);
            return [obj].concat(l);
        });
    };

    const sortForm = (result: unknown[]) =>
        result
            .map((item, index) => ({ key: index, item })) // add index into obj
            .sort((a, b) => getIndex(a.key) - getIndex(b.key)) // sort based on the index of table
            .filter(item => !!item.item) // remove undefined(s)
            .map(item => item.item); // retrive the data

    const shift = () => {
        // remove keys if necessary
        try {
            keyList.current = keyList.current.slice(1, keyList.current.length);
        } catch (e) {
            console.error(e);
        }
        setList(l => l.slice(1, l.length));
    };

    return {
        list,
        insert,
        merge,
        replace,
        remove,
        getKey,
        getIndex,
        move,
        push,
        pop,
        unshift,
        shift,
        sortForm,
        resetList
    };
};
